/*
 * Written by Doug Lea with assistance from members of JCP JSR-166
 * Expert Group and released to the public domain, as explained at
 * http://creativecommons.org/publicdomain/zero/1.0/
 */
package com.nulldev.util.internal.backport.concurrency9.concurrent;

import java.security.AccessControlContext;
import java.security.PrivilegedAction;
import java.util.Objects;
import java.util.concurrent.atomic.AtomicInteger;
import java.util.concurrent.atomic.AtomicLong;

import com.nulldev.util.VariableAPI.RandomUtil;
import com.nulldev.util.internal.unsafecompat.IUnsafe;

/**
 * A random number generator isolated to the current thread.
 *
 * @since 1.7
 * @author Doug Lea
 */
final class TLRandom {
// CVS rev. 1.58

	private static long mix64(long z) {
		z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
		z = (z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L;
		return z ^ (z >>> 33);
	}

	private static int mix32(long z) {
		z = (z ^ (z >>> 33)) * 0xff51afd7ed558ccdL;
		return (int) (((z ^ (z >>> 33)) * 0xc4ceb9fe1a85ec53L) >>> 32);
	}

	private TLRandom() {
	}

	/**
	 * Initialize Thread fields for the current thread. Called only when
	 * Thread.threadLocalRandomProbe is zero, indicating that a thread local seed
	 * value needs to be generated. Note that even though the initialization is
	 * purely thread-local, we need to rely on (static) atomic generators to
	 * initialize the values.
	 */
	static final void localInit() {
		int p = probeGenerator.addAndGet(PROBE_INCREMENT);
		int probe = (p == 0) ? 1 : p; // skip 0
		long seed = mix64(seeder.getAndAdd(SEEDER_INCREMENT));
		utilizeSeed(seed);
		setThreadLocalRandomProbe(probe);
	}

	// Within-package utilities

	/*
	 * Descriptions of the usages of the methods below can be found in the classes
	 * that use them. Briefly, a thread's "probe" value is a non-zero hash code that
	 * (probably) does not collide with other existing threads with respect to any
	 * power of two collision space. When it does collide, it is pseudo-randomly
	 * adjusted (using a Marsaglia XorShift). The nextSecondarySeed method is used
	 * in the same contexts as ThreadLocalRandom, but only for transient usages such
	 * as random adaptive spin/block sequences for which a cheap RNG suffices and
	 * for which it could in principle disrupt user-visible statistical properties
	 * of the main ThreadLocalRandom if we were to use it.
	 */

	/**
	 * Returns the probe value for the current thread without forcing
	 * initialization. Note that invoking ThreadLocalRandom.current() can be used to
	 * force initialization on zero return.
	 */
	static final int getProbe() {
		return getThreadLocalRandomProbe();
	}

	/**
	 * Pseudo-randomly advances and records the given probe value for the given
	 * thread.
	 */
	static final int advanceProbe(int probe) {
		probe ^= probe << 13; // xorshift
		probe ^= probe >>> 17;
		probe ^= probe << 5;
		setThreadLocalRandomProbe(probe);
		return probe;
	}

	/**
	 * Returns the pseudo-randomly initialized or updated secondary seed.
	 */
	static final int nextSecondarySeed() {
		int r;
		if ((r = getThreadLocalRandomSecondarySeed()) != 0) {
			r ^= r << 13; // xorshift
			r ^= r >>> 17;
			r ^= r << 5;
		} else if ((r = mix32(seeder.getAndAdd(SEEDER_INCREMENT))) == 0) {
			r = 1; // avoid zero
		}
		setThreadLocalRandomSecondarySeed(r);
		return r;
	}

	private static void utilizeSeed(long seed) {
		Objects.requireNonNull(Long.valueOf(seed));
	}

	private static int getThreadLocalRandomProbe() {
		return U.getInt(Thread.currentThread(), PROBE);
	}

	private static void setThreadLocalRandomProbe(int probe) {
		U.putInt(Thread.currentThread(), PROBE, probe);
	}

	private static int getThreadLocalRandomSecondarySeed() {
		return U.getInt(Thread.currentThread(), SECONDARY);
	}

	private static void setThreadLocalRandomSecondarySeed(int secondary) {
		U.putInt(Thread.currentThread(), SECONDARY, secondary);
	}

	// Support for other package-private ThreadLocal access

	/**
	 * Erases ThreadLocals by nulling out Thread maps.
	 */
	static final void eraseThreadLocals(Thread thread) {
		U.putObject(thread, THREADLOCALS, null);
		U.putObject(thread, INHERITABLETHREADLOCALS, null);
	}

	static final void setInheritedAccessControlContext(Thread thread, AccessControlContext acc) {
		U.putOrderedObject(thread, INHERITEDACCESSCONTROLCONTEXT, acc);
	}

	static final void setContextClassLoader(Thread thread, ClassLoader ccl) {
		U.putObject(thread, CCL, ccl);
	}

	// Static initialization

	/**
	 * The increment for generating probe values.
	 */
	private static final int PROBE_INCREMENT = 0x9e3779b9;

	/**
	 * The increment of seeder per new instance.
	 */
	private static final long SEEDER_INCREMENT = 0xbb67ae8584caa73bL;

	// Unsafe mechanics
	private static final IUnsafe U = UnsafeAccess.unsafe;
	private static final long THREADLOCALS;
	private static final long INHERITABLETHREADLOCALS;
	private static final long INHERITEDACCESSCONTROLCONTEXT;
	private static final long CCL;
	private static final long PROBE;
	private static final long SECONDARY;
	static {
		try {
			THREADLOCALS = U.objectFieldOffset(Thread.class.getDeclaredField("threadLocals"));
			INHERITABLETHREADLOCALS = U.objectFieldOffset(Thread.class.getDeclaredField("inheritableThreadLocals"));
			INHERITEDACCESSCONTROLCONTEXT = U.objectFieldOffset(Thread.class.getDeclaredField("inheritedAccessControlContext"));
			CCL = U.objectFieldOffset(Thread.class.getDeclaredField("contextClassLoader"));
			PROBE = U.objectFieldOffset(Thread.class.getDeclaredField("threadLocalRandomProbe"));
			SECONDARY = U.objectFieldOffset(Thread.class.getDeclaredField("threadLocalRandomSecondarySeed"));
		} catch (Exception e) {
			throw new ExceptionInInitializerError(e);
		}
	}

	/** Generates per-thread initialization/probe field */
	private static final AtomicInteger probeGenerator = new AtomicInteger();

	/**
	 * The next seed for default constructors.
	 */
	private static final AtomicLong seeder = new AtomicLong(mix64(System.currentTimeMillis()) ^ mix64(System.nanoTime()));

	// at end of <clinit> to survive static initialization circularity
	static {
		if (java.security.AccessController.doPrivileged(new PrivilegedAction<Boolean>() {
			@Override
			public Boolean run() {
				return Boolean.getBoolean("java.util.secureRandomSeed");
			}
		})) {
			byte[] seedBytes = RandomUtil.bytes(8);
			long s = (long) seedBytes[0] & 0xffL;
			for (int i = 1; i < 8; ++i) {
				s = (s << 8) | ((long) seedBytes[i] & 0xffL);
			}
			seeder.set(s);
		}
	}
}
